#include <string.h>

#include "els.h"
#include "els_heap.h"
#include "els_func.h"
#include "els_gc.h"
#include "els_mem.h"
#include "els_object.h"
#include "els_vmhost.h"
#include "els_string.h"
#include "els_unit.h"
#include "els_vmcore.h"

#define GCscale(x) ((int)((x) >> 10))
#define GCunscale(x) ((unsigned long)(x) << 10)
#define Index(L, i) ((i) >= 0 ? (L->Cbase + ((i)-1)) : (L->top + (i)))
#define api_incr_top(L) incr_top
#define els_API_index(l, index) Index(l, index)

extern void els_API_pushobject(els_VmObj *L, const StackObj *o);
extern int parse_file(els_VmObj *L, const char *filename);
extern int parse_buffer(els_VmObj *L, const char *buff, size_t size, const char *name);

StackObj *els_getstackobj(els_VmObj *L, int index)
{
    if (index >= 0)
    {
        StackObj *o = L->Cbase + (index - 1);
        if (o >= L->top)
            o->ttype = ELS_TYPE_NULL;
        return o;
    }
    else
        return L->top + index;
}

void els_API_pushobject(els_VmObj *L, const StackObj *o)
{
    *L->top = *o;
    incr_top;
}

/*VM api*/
ELS_API els_VmObj *els_createvm(int stacksize);
ELS_API void els_closevm(els_VmObj *L);
char* els_lib_win_togbk(const char * str);
ELS_API void els_error(els_VmObj *L, const char *s)
{
    printf("EasyLosu Script Error: \t%s\n", s);
    els_Heap_breakrun(L, ELS_ERRRUN);
}

/*exec api*/
ELS_API int els_call(els_VmObj *L, int nargs, int nresults);

ELS_API int els_dostring(els_VmObj *L, const char *str)
{
    int i;
    if (!(i = parse_buffer(L, str, strlen(str), str)))
        return els_call(L, 0, ELS_MULTRET);
    return i;
}

ELS_API int els_dofile(els_VmObj *L, const char *filename)
{
    int i;
    if (!(i = parse_file(L, filename)))
        return els_call(L, 0, ELS_MULTRET);
    return i;
}

/*api type is*/
ELS_API int els_type(els_VmObj *L, int index)
{
    return ttype(els_getstackobj(L, index));
}

/*api stack get*/
ELS_API double els_getnum(els_VmObj *L, int index)
{
    Els_vminline_obj o = els_getstackobj(L, index);
    return (o == NULL || tonumber(o)) ? 0 : nvalue(o);
}

ELS_API const char *els_getstr(els_VmObj *L, int index)
{
    Els_vminline_obj o = els_getstackobj(L, index);
    return (o == NULL || tostring(L, o)) ? "" : svalue(o);
}

ELS_API els_CFunction els_getfunc(els_VmObj *L, int index)
{
    Els_vminline_obj o = els_getstackobj(L, index);
    return (o == NULL || !iscfunction(o)) ? NULL : clvalue(o)->f.c;
}

ELS_API void els_getvmvalue(els_VmObj *L, const char *name)
{
    Els_vminline_obj top = L->top;
    *top = *els_Vmcore_getglobal(L, els_string_new(L, name));
    L->top = top;
    api_incr_top(L);
}

ELS_API void els_pushnull(els_VmObj *L)
{
    ttype(L->top) = ELS_TYPE_NULL;
    api_incr_top(L);
}

ELS_API void els_pushnum(els_VmObj *L, double n)
{
    nvalue(L->top) = n;
    ttype(L->top) = ELS_TYPE_NUMBER;
    api_incr_top(L);
}

ELS_API void els_pushstr(els_VmObj *L, const char *s)
{
    if (s == NULL)
        els_pushnull(L);
    else
    {
        tsvalue(L->top) = els_string_newlstr(L, s, strlen(s));
        ttype(L->top) = ELS_TYPE_STRING;
        api_incr_top(L);
    }
}

ELS_API void els_pushvmvalue(els_VmObj *L, const char *name)
{
    Els_vminline_obj top = L->top;
    els_Vmcore_setglobal(L, els_string_new(L, name));
    L->top = top - 1; /* remove element from the top */
}

ELS_API void els_pushcfunciotn(els_VmObj *L, els_CFunction fn, int n)
{
    els_Vmcore_C_csfunciotn(L, fn, n);
}

ELS_API void els_settop(els_VmObj *L, int index)
{
    if (index >= 0)
        els_Heap_adjusttop(L, L->Cbase, index);
    else
        L->top = L->top + index + 1;
}

ELS_API void els_register(els_VmObj *l, const char *n, els_CFunction  f)
{
    els_pushcfunciotn(l, f, 0);
    els_pushvmvalue(l, n);
}
